# _*_ coding:utf-8 _*_
# @Time  : 2022.06.04
# @Author: zizlee
import datetime
import re
import pandas as pd
import numpy as np
from typing import List
from db import RuizyEDBConnection
from v1_all_api import constant


def extra_formula_index(formula_explain):  # 提取公式的指标列
    table_name_str = re.sub(r'[()]', '', formula_explain)
    table_name_str = re.sub(r'[*/+-]', ',', table_name_str)
    parse_index = table_name_str.split(',')
    return list(filter(lambda x: x.startswith('wind_') or x.startswith('ths_') or x.startswith('rj_'), parse_index))


# 数据表具体数据处理
async def get_index_formula_dataframe(column_templates: List[dict], na_fill='NAN',
                                      start_date: datetime.date = None, end_date: datetime.date = None):
    # 解析数据中的公式，得到需要的表
    need_tables = []
    for item in column_templates:
        formula_indexes = extra_formula_index(item['formula'])
        for s in formula_indexes:
            if s not in need_tables:
                need_tables.append(s)

        # formula = item['formula']
        # table_name_str = re.sub(r'[()]', '', formula)
        # table_name_str = re.sub(r'[*/+-]', ',', table_name_str)
        # for s in table_name_str.split(','):
        #     if (s.startswith('wind_') or s.startswith('ths_') or s.startswith('rj_')) and s not in need_tables:
        #         need_tables.append(s)

    # 查询所需表的数据，进行数据处理返回
    db_conn = RuizyEDBConnection()
    query_ret = await db_conn.query_formula(need_tables)
    if not query_ret:
        return pd.DataFrame(), []
    # 拼接need_tables里面的数据为DataFrame
    df = pd.DataFrame(query_ret.get(need_tables[0], []))
    df.rename(columns={'datavalue': need_tables[0]}, inplace=True)
    for index, table in enumerate(need_tables[1:]):
        tdf = pd.DataFrame(query_ret.get(table, []))
        tdf.rename(columns={'datavalue': table}, inplace=True)
        df = pd.merge(df, tdf, how='outer', on='datadate')
    df.sort_values(by='datadate', inplace=True, ascending=True)
    if start_date:
        df = df[df['datadate'] >= start_date]
    if end_date:
        df = df[df['datadate'] <= end_date]
    # print(df)
    # 表头和计算表格数据
    table_headers = []
    # first_row = []
    for index, indication in enumerate(column_templates):
        # print(indication)
        # print(indication['formula'])
        # 此处加入is_diff只是制作图形时，参数需要这个,不然表头并不需要
        # table_headers.append({'name': indication['name'], 'prop': f'c_{index}', 'is_diff': indication.get('is_diff', False),
        #                       'unit': indication['unit'], 'formula': indication['formula']})
        # 修改为放入原先指标数据，返回后也好处理，这里只增加prop和is_diff
        table_headers.append({'prop': f'c_{index}', 'is_diff': indication.get('is_diff', False), **indication})
        # 公式计算出目标数据
        # df[f'c_{index}'] = df[indication['formula']]
        indication['prop'] = f'c_{index}'
        df.eval(f'c_{index} = {indication["formula"]}', inplace=True)  # pandas表达式计算
        if len(indication['formula']) > 14:  # 计算公式计算的保留2位小数
            df = df.round({f'c_{index}': 2})
        if indication.get('is_diff'):  # 是计算同比的
            df[f'c_{index}'] = df[f'c_{index}'].diff().round(2)
        if indication.get('noZero'):
            # 公式里数据列有含0的，那么计算结果也应该相应的改为0
            formula_indexes = extra_formula_index(indication['formula'])
            for f_index in formula_indexes:  # df[f_index]里为0的行，相应c_index也应改为0
                df.loc[df[f_index] == 0, f'c_{index}'] = 0
            df.replace({f'c_{index}': {0: np.NAN}}, inplace=True)  # 将数据0替换为NAN
    df_columns = df.columns.tolist()
    for col in df_columns:
        if col == 'datadate' or col.startswith('c_'):
            continue
        del df[col]
    df.fillna(na_fill, inplace=True)
    return df, table_headers


# 过滤掉非管理员时那些设为私有的数据
def private_handler(item, user_obj):
    if user_obj['role_code'] == constant.ADMIN_ROLE_CODE:
        return True
    return True if item['is_open'] == 1 else user_obj['uid'] == item['user_id']


# 验证指标运算公式
def formula_is_correct(formula: str):
    test_formula = formula
    correct = True
    indexes = extra_formula_index(formula)
    for i in indexes:
        if len(i) < 8 or len(i) > 14:
            correct = False
            break
        test_formula = test_formula.replace(i, '1')
    if correct:
        try:
            eval(test_formula)
        except NameError:
            correct = False
        except SyntaxError:
            correct = False
    return correct
